﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Soft
{

    /// <summary>
    /// 时间周期
    /// </summary>
    public class TimeUnitPeriod
    {
        public TimeUnitPeriod(DateTime sampling, TimeUnit unit, int length = 1, bool originAlignment = true)
        {
            if (unit == TimeUnit.NotSet)
            {
                throw new Exception("无效的时间单位参数。");
            }
            if (length < 1)
            {
                throw new ArgumentOutOfRangeException("count", "值必须大于零。");
            }
            this.mSampling = sampling;
            this.Unit = unit;
            this.Length = Convert.ToUInt16(length);
            this.mOriginAlignment = originAlignment;
            this.Reset();
        }

        public TimeUnitPeriod(DateTime sampling, TimeUnitItem item, bool originAlignment = true) : this(sampling, item.Unit, item.Length, true)
        {
        }

        private DateTime get_StartTimeOfSingleUnit()
        {
            DateTime result = this.mSampling;
            try
            {
                switch (this.Unit)
                {
                    case TimeUnit.Minute:
                        result = this.mSampling.AddSeconds((double)(-(double)this.mSampling.Second));
                        break;
                    case TimeUnit.Hour:
                        result = new DateTime(this.mSampling.Year, this.mSampling.Month, this.mSampling.Day, this.mSampling.Hour, 0, 0);
                        break;
                    case TimeUnit.Day:
                        result = this.mSampling.Date;
                        break;
                    case TimeUnit.Week:
                        {
                            int dayOfWeek = (int)this.mSampling.DayOfWeek;
                            int firstDayOfWeek = (int)FactoryAdvisorPlatformGlobalSet.FirstDayOfWeek;
                            int num = firstDayOfWeek - dayOfWeek;
                            if (num > 0)
                            {
                                num -= 7;
                            }
                            result = this.mSampling.Date.AddDays((double)num);
                            break;
                        }
                    case TimeUnit.Month:
                        result = new DateTime(this.mSampling.Year, this.mSampling.Month, 1);
                        break;
                    case TimeUnit.Quarter:
                        result = new DateTime(this.mSampling.Year, this.mSampling.Month - (this.mSampling.Month - 1) % 3, 1);
                        break;
                    case TimeUnit.Year:
                        result = new DateTime(this.mSampling.Year, 1, 1);
                        break;
                    case TimeUnit.Era:
                        result = new DateTime(this.mSampling.Year / 100 * 100 + 1, 1, 1);
                        break;
                }
            }
            catch (Exception ex)
            {
                throw new Exception("处理日期时间时出现错误：" + ex.Message);
            }
            return result;
        }

        private DateTime get_StartTimeOfMultipleUnit()
        {
            DateTime result = DateTime.MinValue;
            TimeSpan timeSpan = this.Sampling.Subtract(DateTime.MinValue);
            switch (this.Unit)
            {
                case TimeUnit.Minute:
                    {
                        ulong num = Convert.ToUInt64(Math.Floor(timeSpan.TotalMinutes / (double)this.Length)) * (ulong)this.Length;
                        result = result.AddMinutes(num);
                        break;
                    }
                case TimeUnit.Hour:
                    {
                        ulong num = Convert.ToUInt64(Math.Floor(timeSpan.TotalHours / (double)this.Length)) * (ulong)this.Length;
                        result = result.AddHours(num);
                        break;
                    }
                case TimeUnit.Day:
                    {
                        ulong num = Convert.ToUInt64(Math.Floor(timeSpan.TotalDays / (double)this.Length)) * (ulong)this.Length;
                        result = result.AddDays(num);
                        break;
                    }
                case TimeUnit.Week:
                    {
                        ulong num = Convert.ToUInt64(Math.Floor(timeSpan.TotalDays / 7.0 / (double)this.Length)) * (ulong)this.Length;
                        result = result.AddDays(num * 7UL);
                        break;
                    }
                case TimeUnit.Month:
                    {
                        ulong num = Convert.ToUInt64((this.Sampling.Year * 12 + this.Sampling.Month) / (int)this.Length) * (ulong)this.Length;
                        result = result.AddMonths(Convert.ToInt32(num));
                        break;
                    }
                case TimeUnit.Quarter:
                    {
                        ulong num = Convert.ToUInt64((this.Sampling.Year * 12 + this.Sampling.Month) / 3 / (int)this.Length) * (ulong)this.Length;
                        result = result.AddMonths(Convert.ToInt32(num * 3UL));
                        break;
                    }
                case TimeUnit.Year:
                    {
                        ulong num = Convert.ToUInt64(this.Sampling.Year / (int)this.Length) * (ulong)this.Length;
                        result = result.AddYears(Convert.ToInt32(num));
                        break;
                    }
                case TimeUnit.Era:
                    {
                        ulong num = Convert.ToUInt64(this.Sampling.Year / 100 / (int)this.Length) * (ulong)this.Length;
                        result = result.AddYears(Convert.ToInt32(num * 100UL));
                        break;
                    }
                default:
                    result = result.AddSeconds(Convert.ToUInt64(timeSpan.TotalSeconds / (double)this.Length));
                    break;
            }
            return result;
        }

        private DateTime get_StartTime()
        {
            if (this.Length > 1 && this.OriginAlignment)
            {
                return this.get_StartTimeOfMultipleUnit();
            }
            return this.get_StartTimeOfSingleUnit();
        }

        private DateTime get_EndTime(DateTime startTime)
        {
            DateTime result = startTime;
            switch (this.Unit)
            {
                case TimeUnit.Minute:
                    result = result.AddMinutes((double)this.Length).AddSeconds(-1.0);
                    break;
                case TimeUnit.Hour:
                    result = result.AddHours((double)this.Length).AddSeconds(-1.0);
                    break;
                case TimeUnit.Day:
                    result = result.AddDays((double)this.Length).AddSeconds(-1.0);
                    break;
                case TimeUnit.Week:
                    result = result.AddDays((double)(this.Length * 7)).AddSeconds(-1.0);
                    break;
                case TimeUnit.Month:
                    result = result.AddMonths((int)this.Length).AddSeconds(-1.0);
                    break;
                case TimeUnit.Quarter:
                    result = result.AddMonths((int)(this.Length * 3)).AddSeconds(-1.0);
                    break;
                case TimeUnit.Year:
                    result = result.AddYears((int)this.Length).AddSeconds(-1.0);
                    break;
                case TimeUnit.Era:
                    result = result.AddYears((int)(this.Length * 100)).AddSeconds(-1.0);
                    break;
                default:
                    result = result.AddSeconds((double)(this.Length - 1));
                    break;
            }
            return result;
        }

        private void Reset()
        {
            try
            {
                DateTime startTime = this.get_StartTime();
                this.Range = new DateTimeRange(startTime, this.get_EndTime(startTime));
            }
            catch (Exception ex)
            {
                throw new Exception("处理日期时间时出现错误：" + ex.Message);
            }
        }

        public void Add(int length = 1)
        {
            if (length == 0)
            {
                return;
            }
            try
            {
                switch (this.Unit)
                {
                    case TimeUnit.Second:
                        this.mSampling = this.mSampling.AddSeconds((double)(length * (int)this.Length));
                        break;
                    case TimeUnit.Minute:
                        this.mSampling = this.mSampling.AddMinutes((double)(length * (int)this.Length));
                        break;
                    case TimeUnit.Hour:
                        this.mSampling = this.mSampling.AddHours((double)(length * (int)this.Length));
                        break;
                    case TimeUnit.Day:
                        this.mSampling = this.mSampling.AddDays((double)(length * (int)this.Length));
                        break;
                    case TimeUnit.Week:
                        this.mSampling = this.mSampling.AddDays((double)(length * (int)this.Length * 7));
                        break;
                    case TimeUnit.Month:
                        this.mSampling = this.mSampling.AddMonths(Convert.ToInt32(length * (int)this.Length));
                        break;
                    case TimeUnit.Quarter:
                        this.mSampling = this.mSampling.AddMonths(Convert.ToInt32(length * (int)this.Length) * 3);
                        break;
                    case TimeUnit.Year:
                        this.mSampling = this.mSampling.AddYears(Convert.ToInt32(length * (int)this.Length));
                        break;
                    case TimeUnit.Era:
                        this.mSampling = this.mSampling.AddYears(Convert.ToInt32(length * (int)this.Length) * 100);
                        break;
                    default:
                        return;
                }
                this.Reset();
            }
            catch (Exception ex)
            {
                throw new Exception("处理日期时间时出现错误：" + ex.Message);
            }
        }

        public bool OriginAlignment
        {
            get
            {
                return this.mOriginAlignment;
            }
            set
            {
                if (this.mOriginAlignment != value)
                {
                    this.mOriginAlignment = value;
                    this.Reset();
                }
            }
        }

        public DateTime Sampling
        {
            get
            {
                return this.mSampling;
            }
            set
            {
                if (this.mSampling != value)
                {
                    this.mSampling = value;
                    this.Reset();
                }
            }
        }

        /// <summary>
        /// 时间片段
        /// </summary>
        public TimeUnit Unit { get; private set; }

        public ushort Length { get; private set; }

        /// <summary>
        /// 时间范围
        /// </summary>
        public DateTimeRange Range { get; private set; }

        public static List<DateTimeRange> BuildRangeList(DateTime startTime, DateTime endTime, TimeUnitItem intervalUnit)
        {
            List<DateTimeRange> list = new List<DateTimeRange>();
            TimeUnitPeriod timeUnitPeriod = new TimeUnitPeriod(startTime, intervalUnit, true);
            while (timeUnitPeriod.Range.Start <= endTime)
            {
                list.Add(timeUnitPeriod.Range);
                timeUnitPeriod.Add(1);
            }
            return list;
        }

        private bool mOriginAlignment;

        private DateTime mSampling;

    }
}
